package com.ds.infrastructure.hyperspace.container.internal.util;

import com.ds.infrastructure.hyperspace.container.internal.http.NettyHttpHeaderNames;
import com.ds.infrastructure.hyperspace.container.internal.http.NettyHttpHeaderValues;
import com.ds.infrastructure.hyperspace.container.internal.http2.HyperspaceHttp2FrameWrapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http2.*;
import io.netty.util.AsciiString;
import org.apache.commons.lang3.StringUtils;

import static com.ds.infrastructure.hyperspace.container.internal.http2.HyperspaceHttp2FrameTypeEnum.DATA;
import static com.ds.infrastructure.hyperspace.container.internal.http2.HyperspaceHttp2FrameTypeEnum.HEADERS;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @Author: yaozh
 * @Description:
 */
public class Http2Util {
    public static final int VALID_RESPONSE_CODE_THRESHOLD = 399;

    public static final String GRPC_PATH_SEPARATOR = "/";

    public static final String GRPC_SERVICE_ROUTER_PREFIX = "grpc:";

    private static final Http2HeadersFrame SUCCESS_HTTP2_HEADERS_FRAME = newSuccessHttp2HeadersFrame();

    private static final Http2HeadersFrame END_HTTP2_HEADERS_FRAME = newEndHttp2HeadersFrame();

    private Http2Util() {
    }

    public static AsciiString newKey(String value) {
        return newAsciiString(value, true);
    }

    public static AsciiString newValue(String value) {
        return newAsciiString(value, false);
    }

    private static AsciiString newAsciiString(String value, boolean isLowerCase) {
        if (value == null) {
            return null;
        }
        if (isLowerCase) {
            return new AsciiString(value.toLowerCase());
        }
        return new AsciiString(value);
    }

    public static boolean isErrorCode(int code) {
        return code > VALID_RESPONSE_CODE_THRESHOLD;
    }

    /**
     * grpc ping response data
     *
     * @return
     */
    public static ByteBuf getGrpcPingResponseData() {
        byte[] value = String.valueOf(System.currentTimeMillis()).getBytes(UTF_8);
        ByteBuf buffer = Unpooled.buffer();
        // compressed flag 为0
        buffer.writeByte((byte) 0);
        // 设置message长度
        buffer.writeBytes(intToByteArray(value.length + 2));
        // 设置field number和wire type
        buffer.writeByte((byte) 10);
        // 设置value长度
        buffer.writeByte((byte) value.length);
        buffer.writeBytes(value);
        return buffer;
    }

    private static byte[] intToByteArray(int a) {
        return new byte[]{
                (byte) ((a >> 24) & 0xFF),
                (byte) ((a >> 16) & 0xFF),
                (byte) ((a >> 8) & 0xFF),
                (byte) (a & 0xFF)
        };
    }


    /**
     * Success header frame
     *
     * @param streamId
     * @return
     */
    public static HyperspaceHttp2FrameWrapper newSuccessHeadersFrameWrapper(int streamId) {
        return new HyperspaceHttp2FrameWrapper(HEADERS, streamId, SUCCESS_HTTP2_HEADERS_FRAME);
    }

    private static Http2HeadersFrame newSuccessHttp2HeadersFrame() {
        Http2Headers http2Header = new DefaultHttp2Headers();
        http2Header.add(NettyHttpHeaderNames.STATUS, NettyHttpHeaderValues.HEADER_STATUS_SUCCESS);
        http2Header.add(NettyHttpHeaderNames.CONTENT_TYPE, NettyHttpHeaderValues.CONTENT_TYPE_GRPC);
        http2Header.add(NettyHttpHeaderNames.GRPC_STATUS, NettyHttpHeaderValues.GRPC_STATUS_SUCCESS);
        http2Header.add(NettyHttpHeaderNames.GRPC_ENCODING, NettyHttpHeaderValues.GRPC_ENCODING_IDENTITY);
        http2Header.add(NettyHttpHeaderNames.GRPC_ACCEPT_ENCODING, NettyHttpHeaderValues.GRPC_ACCEPT_ENCODING_GZIP);
        return new DefaultHttp2HeadersFrame(http2Header, false);
    }

    /**
     * Data frame
     *
     * @param streamId
     * @param data
     * @return
     */
    public static HyperspaceHttp2FrameWrapper newDataFrameWrapper(int streamId, ByteBuf data) {
        DefaultHttp2DataFrame http2Data = new DefaultHttp2DataFrame(data);
        return new HyperspaceHttp2FrameWrapper(DATA, streamId, http2Data);
    }

    /**
     * @param streamId
     * @return
     */
    public static HyperspaceHttp2FrameWrapper newEndHeadersFrameWrapper(int streamId) {
        return new HyperspaceHttp2FrameWrapper(HEADERS, streamId, END_HTTP2_HEADERS_FRAME);
    }

    private static Http2HeadersFrame newEndHttp2HeadersFrame() {
        Http2Headers http2Header = new DefaultHttp2Headers();
        http2Header.add(NettyHttpHeaderNames.GRPC_STATUS, NettyHttpHeaderValues.GRPC_STATUS_SUCCESS);
        return new DefaultHttp2HeadersFrame(http2Header, true);
    }

    /**
     * @param path
     * @return
     */
    public static String getGrpcService(String path) {
        String service = StringUtils.substringBeforeLast(path, GRPC_PATH_SEPARATOR);
        if (StringUtils.isBlank(service)) {
            return null;
        }
        return service;
    }

    /**
     * @param service
     * @return
     */
    public static String getGrpcServiceRouterName(String service) {
        return GRPC_SERVICE_ROUTER_PREFIX + service;
    }
}
